package com.tongtong.middleware.blockchain.service;

import com.tongtong.middleware.blockchain.client.web3j.WEB3JService;
import com.tongtong.middleware.blockchain.client.web3j.contract.ContractDecoder;
import com.tongtong.middleware.blockchain.client.web3j.contract.ERC1155Input;
import com.tongtong.middleware.vo.response.SignTxRes;
import lombok.extern.slf4j.Slf4j;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.cloud.context.config.annotation.RefreshScope;
import org.springframework.stereotype.Service;
import org.web3j.abi.datatypes.Type;

import java.io.IOException;
import java.math.BigInteger;
import java.util.List;

/**
 * @author wangqi
 */
@Service
@Slf4j
@RefreshScope
public class ERC1155Service {
    @Autowired
    WEB3JService web3jService;
    @Value("${eth.managerAddr}")
    private String managerAddr;
    @Value("${eth.managerKey}")
    private String managerKey;
    @Value("${eth.erc1155Addr}")
    private String contractAddr;
    @Autowired
    MetadataService metadataService;

    /**
     * 铸造NFT
     */
    public SignTxRes mintNFT(String to, BigInteger tokenId, BigInteger amount) {
        String input = ERC1155Input.mint(to, tokenId, amount);
        SignTxRes res;
        try {
            res = web3jService.signTx(contractAddr, input, managerKey);
        } catch (IOException e) {
            throw new RuntimeException(e);
        }
        String tokenUri = metadataService.metadataByTokenId(tokenId.toString());
        if (tokenUri.length() < 10) {
            log.error("ERC1155Service mintNFT metadataByTokenId error");
        }
        return res;
    }
    /**
     * 批量铸造NFT
     */
    public SignTxRes mintBatchNFT(String to, List<BigInteger> ids, List<BigInteger> values) {
        String input = ERC1155Input.mintBatch(to, ids, values);
        SignTxRes res;
        try {
            res = web3jService.signTx(contractAddr, input, managerKey);
        } catch (IOException e) {
            throw new RuntimeException(e);
        }
        return res;
    }
    /**
     * 转移NFT
     */
    public SignTxRes transfer(String from, String to, BigInteger tokenId, BigInteger amount) {
        String input = ERC1155Input.safeTransferFrom(from, to, tokenId, amount, "");
        // 如果from不等于管理员，则判断是否授权给管理员，如果没授权则先授权
        if (!managerAddr.equalsIgnoreCase(from)) {
            String approvedStatus = isApprovedForAll(from, managerAddr);
            if ("0".equals(approvedStatus)) {
                approval(from);
            }
        }
        SignTxRes res;
        try {
            res = web3jService.signTx(contractAddr, input, managerKey);
        } catch (IOException e) {
            throw new RuntimeException(e);
        }
        return res;
    }
    /**
     * 批量转移NFT
     */
    public SignTxRes transferBatch(String from, String to, List<BigInteger> ids, List<BigInteger> amounts) {
        String input = ERC1155Input.safeBatchTransferFrom(from, to, ids, amounts, "");
        // 如果from不等于管理员，则判断是否授权给管理员，如果没授权则先授权
        if (!managerAddr.equalsIgnoreCase(from)) {
            String approvedStatus = isApprovedForAll(from, managerAddr);
            if ("0".equals(approvedStatus)) {
                approval(from);
            }
        }
        SignTxRes res;
        try {
            res = web3jService.signTx(contractAddr, input, managerKey);
        } catch (IOException e) {
            throw new RuntimeException(e);
        }
        return res;
    }
    /**
     * 获取授权
     */
    public SignTxRes approval(String account) {
        String input = ERC1155Input.approval(account);
        SignTxRes res;
        try {
            res = web3jService.signTx(contractAddr, input, managerKey);
        } catch (IOException e) {
            throw new RuntimeException(e);
        }
        return res;
    }
    /**
     * 销毁NFT
     */
    public SignTxRes burnNFT(String account, BigInteger tokenId, BigInteger amount) {
        String input = ERC1155Input.burn(account, tokenId, amount);
        SignTxRes res;
        try {
            res = web3jService.signTx(contractAddr, input, managerKey);
        } catch (IOException e) {
            throw new RuntimeException(e);
        }
        return res;
    }
    /**
     * 批量销毁NFT
     */
    public SignTxRes burnBatchNFT(String account, List<BigInteger> ids, List<BigInteger> values) {
        String input = ERC1155Input.burnBatch(account, ids, values);
        SignTxRes res;
        try {
            res = web3jService.signTx(contractAddr, input, managerKey);
        } catch (IOException e) {
            throw new RuntimeException(e);
        }
        return res;
    }
    /**
     * 查询指定用户拥有指定TokenID的数量
     */
    public String balanceOf(String account, BigInteger tokenId) {
        String input = ERC1155Input.balanceOf(account, tokenId);
        String res = null;
        try {
            // 查询合约，随便用个地址就行
            res = web3jService.call("0x0000000000000000000000000000000000000000", contractAddr, input);
        } catch (IOException ignored) {
        }
        List<Type> decode = ContractDecoder.decode("uint256", res);
        return decode.get(0).getValue().toString();
    }
    /**
     * 查询用户是否被指定操作者授权
     */
    public String isApprovedForAll(String account, String operator) {
        String input = ERC1155Input.isApprovedForAll(account, operator);
        String res;
        try {
            // 查询合约，随便用个地址就行
            res = web3jService.call("0x0000000000000000000000000000000000000000", contractAddr, input);
        } catch (IOException e) {
            throw new RuntimeException(e);
        }
        List<Type> decode = ContractDecoder.decode("uint256", res);
        return decode.get(0).getValue().toString();
    }
    /**
     * 查询指定TokenID的TokenURI
     */
    public String uri(BigInteger tokenId) {
        String input = ERC1155Input.uri(tokenId);
        String res;
        try {
            // 查询合约，随便用个地址就行
            res = web3jService.call("0x0000000000000000000000000000000000000000", contractAddr, input);
        } catch (IOException e) {
            throw new RuntimeException(e);
        }
        List<Type> decode = ContractDecoder.decode("string", res);
        return decode.get(0).getValue().toString();
    }
}
